package cz.mzk.kramerius.rukopisyimport.foxml;

public class RomanNumeral {

    private final int num;   // The number represented by this Roman numeral.
    
    /* The following arrays are used by the toString() function to construct
    the standard Roman numeral representation of the number.  For each i,
    the number numbers[i] is represented by the corresponding string, letters[i].
     */
    private static int[] numbers = {1000, 900, 500, 400, 100, 90,
        50, 40, 10, 9, 5, 4, 1};
    private static String[] letters = {"M", "CM", "D", "CD", "C", "XC",
        "L", "XL", "X", "IX", "V", "IV", "I"};

    /**
     * @throws NumberFormatException if arabic is not in the range 1 to 3999 inclusive.
     * @param arabic
     */
    public RomanNumeral(int arabic) {
        if (arabic < 1) {
            throw new NumberFormatException("Value of RomanNumeral must be positive.");
        }
        if (arabic > 3999) {
            throw new NumberFormatException("Value of RomanNumeral must be 3999 or less.");
        }
        num = arabic;
    }

    /**
     * Creates the Roman number with the given representation. Both upper and lower case letters are allowed.
     * @param roman
     * @throws NumberFormatException if the parameter is not a legal Roman numeral
     */
    public RomanNumeral(String roman) {
        if (roman.length() == 0) {
            throw new NumberFormatException("An empty string does not define a Roman numeral.");
        }
        roman = roman.toUpperCase();
        int i = 0;       // A position in the string, roman;
        int arabic = 0;  // Arabic numeral equivalent of the part of the string that has been converted so far.
        while (i < roman.length()) {
            char letter = roman.charAt(i);        // Letter at current position in string.
            int number = letterToNumber(letter);  // Numerical equivalent of letter.
            if (number < 0) {
                throw new NumberFormatException("Illegal character \"" + letter + "\" in roman numeral.");
            }
            i++;  // Move on to next position in the string
            if (i == roman.length()) {
                // There is no letter in the string following the one we have just processed.
                // So just add the number corresponding to the single letter to arabic.
                arabic += number;
            } else {
                // Look at the next letter in the string.  If it has a larger Roman numeral
                // equivalent than number, then the two letters are counted together as
                // a Roman numeral with value (nextNumber - number).
                int nextNumber = letterToNumber(roman.charAt(i));
                if (nextNumber > number) {
                    // Combine the two letters to get one value, and move on to next position in string.
                    arabic += (nextNumber - number);
                    i++;
                } else {
                    // Don't combine the letters.  Just add the value of the one letter onto the number.
                    arabic += number;
                }
            }
        }
        if (arabic > 3999) {
            throw new NumberFormatException("Roman numeral must have value 3999 or less.");
        }
        num = arabic;
    }

    /**
     * Find the integer value of letter considered as a Roman numeral. The letter must be upper case.
     * @param letter
     * @return integer value of letter considered as a Roman numeral or -1 if letter is not a legal Roman numeral
     */
    private static int letterToNumber(char letter) {
        switch (letter) {
            case 'I':
                return 1;
            case 'V':
                return 5;
            case 'X':
                return 10;
            case 'L':
                return 50;
            case 'C':
                return 100;
            case 'D':
                return 500;
            case 'M':
                return 1000;
            default:
                return -1;
        }
    }

    /**
     * @return the standard representation of this Roman numeral
     */
    @Override
    public String toString() {
        String roman = "";  // The roman numeral.
        int N = num;        // N represents the part of num that still has
        //   to be converted to Roman numeral representation.
        for (int i = 0; i < numbers.length; i++) {
            while (N >= numbers[i]) {
                roman += letters[i];
                N -= numbers[i];
            }
        }
        return roman;
    }

    /**
     *
     * @return the value of this Roman numeral as an int
     */
    public int toInt() {
        return num;
    }
}
